package eval

import (
	"math"
	"math/big"
	"math/cmplx"
	"showen/num"
	"showen/obj"
)

func Exact(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "exact? requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "exact? argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	// Check if the number is exact based on its type
	switch numObj.Type() {
	case num.INTEGER_TYPE, num.RATIONAL_TYPE:
		// Integers and rationals are exact
		return &obj.BooleanObject{Value: true}
	case num.REAL_TYPE:
		// Reals are inexact
		return &obj.BooleanObject{Value: false}
	case num.COMPLEX_TYPE:
		// Complex numbers are exact if both real and imaginary parts are exact
		complexNum := numObj.(num.Complex)
		realPartExact := complexNum.RealPart.Type() == num.INTEGER_TYPE || complexNum.RealPart.Type() == num.RATIONAL_TYPE
		imagPartExact := complexNum.ImagPart.Type() == num.INTEGER_TYPE || complexNum.ImagPart.Type() == num.RATIONAL_TYPE
		return &obj.BooleanObject{Value: realPartExact && imagPartExact}
	}

	return &obj.BooleanObject{Value: false}
}

func Inexact(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "inexact? requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "inexact? argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	// Check if the number is inexact based on its type
	switch numObj.Type() {
	case num.INTEGER_TYPE, num.RATIONAL_TYPE:
		// Integers and rationals are exact
		return &obj.BooleanObject{Value: false}
	case num.REAL_TYPE:
		// Reals are inexact
		return &obj.BooleanObject{Value: true}
	case num.COMPLEX_TYPE:
		// Complex numbers are inexact if either real or imaginary part is inexact
		complexNum := numObj.(num.Complex)
		realPartInexact := complexNum.RealPart.Type() == num.REAL_TYPE
		imagPartInexact := complexNum.ImagPart.Type() == num.REAL_TYPE
		return &obj.BooleanObject{Value: realPartInexact || imagPartInexact}
	}

	return &obj.BooleanObject{Value: false}
}

func Odd(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "odd? requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "odd? argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value
	if numObj.Type() != num.INTEGER_TYPE {
		return &Error{Message: "odd? argument must be an integer"}
	}

	intVal := int64(numObj.(num.Integer))
	return &obj.BooleanObject{Value: intVal%2 != 0}
}

func Even(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "even? requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "even? argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value
	if numObj.Type() != num.INTEGER_TYPE {
		return &Error{Message: "even? argument must be an integer"}
	}
	intVal := int64(numObj.(num.Integer))
	return &obj.BooleanObject{Value: intVal%2 == 0}
}

func Max(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) == 0 {
		return &Error{Message: "max requires at least 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "max arguments must be numbers"}
	}

	maxNum := args[0].(*obj.NumberObject).Value.(num.RNumber)

	for _, arg := range args[1:] {
		if arg.Type() != obj.NUMBER_OBJ {
			return &Error{Message: "max arguments must be numbers"}
		}

		currentNum := arg.(*obj.NumberObject).Value.(num.RNumber)
		if num.RNumber_CMP(maxNum, currentNum) == -1 { // currentNum > maxNum
			maxNum = currentNum
		}
	}

	// Convert RNumber to Number
	var number num.Number
	switch maxNum.Type() {
	case num.INTEGER_TYPE:
		intVal := maxNum.(num.Integer)
		number = intVal
	case num.RATIONAL_TYPE:
		ratVal := maxNum.(num.Rational)
		number = ratVal
	case num.REAL_TYPE:
		realVal := maxNum.(num.Real)
		number = realVal
	}

	return &obj.NumberObject{Value: number}
}

func Min(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) == 0 {
		return &Error{Message: "min requires at least 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "min arguments must be numbers"}
	}

	minNum := args[0].(*obj.NumberObject).Value.(num.RNumber)

	for _, arg := range args[1:] {
		if arg.Type() != obj.NUMBER_OBJ {
			return &Error{Message: "min arguments must be numbers"}
		}

		currentNum := arg.(*obj.NumberObject).Value.(num.RNumber)
		if num.RNumber_CMP(minNum, currentNum) == 1 { // currentNum < minNum
			minNum = currentNum
		}
	}

	// Convert RNumber to Number
	var number num.Number
	switch minNum.Type() {
	case num.INTEGER_TYPE:
		intVal := minNum.(num.Integer)
		number = intVal
	case num.RATIONAL_TYPE:
		ratVal := minNum.(num.Rational)
		number = ratVal
	case num.REAL_TYPE:
		realVal := minNum.(num.Real)
		number = realVal
	}

	return &obj.NumberObject{Value: number}
}

func Abs(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "abs requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "abs argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value.(num.RNumber)

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		intVal := int64(numObj.(num.Integer))
		if intVal < 0 {
			return &obj.NumberObject{Value: num.Integer(-intVal)}
		}
		return &obj.NumberObject{Value: numObj.(num.Integer)}

	case num.RATIONAL_TYPE:
		// num.Rational is an alias of big.Rat; use big.Rat methods
		r := numObj.(num.Rational)
		br := (*big.Rat)(&r)
		if br.Num().Sign() < 0 {
			neg := new(big.Rat).Neg(br)
			return &obj.NumberObject{Value: num.Rational(*neg)}
		}
		return &obj.NumberObject{Value: numObj.(num.Rational)}

	case num.REAL_TYPE:
		realVal := float64(numObj.(num.Real))
		if realVal < 0 {
			return &obj.NumberObject{Value: num.Real(-realVal)}
		}
		return &obj.NumberObject{Value: numObj.(num.Real)}
	}

	// Convert RNumber to Number
	var number num.Number
	switch numObj.Type() {
	case num.INTEGER_TYPE:
		intVal := numObj.(num.Integer)
		number = intVal
	case num.RATIONAL_TYPE:
		ratVal := numObj.(num.Rational)
		number = ratVal
	case num.REAL_TYPE:
		realVal := numObj.(num.Real)
		number = realVal
	}

	return &obj.NumberObject{Value: number}
}

func Quotient(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 2 {
		return &Error{Message: "quotient requires exactly 2 arguments"}
	}

	if args[0].Type() != obj.NUMBER_OBJ || args[1].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "quotient arguments must be numbers"}
	}

	dividend := args[0].(*obj.NumberObject).Value
	divisor := args[1].(*obj.NumberObject).Value

	if dividend.Type() != num.INTEGER_TYPE || divisor.Type() != num.INTEGER_TYPE {
		return &Error{Message: "quotient arguments must be integers"}
	}

	dividendInt := int64(dividend.(num.Integer))
	divisorInt := int64(divisor.(num.Integer))

	if divisorInt == 0 {
		return &Error{Message: "quotient: division by zero"}
	}

	return &obj.NumberObject{Value: num.Integer(dividendInt / divisorInt)}
}

func Remainder(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 2 {
		return &Error{Message: "remainder requires exactly 2 arguments"}
	}

	if args[0].Type() != obj.NUMBER_OBJ || args[1].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "remainder arguments must be numbers"}
	}

	dividend := args[0].(*obj.NumberObject).Value
	divisor := args[1].(*obj.NumberObject).Value

	if dividend.Type() != num.INTEGER_TYPE || divisor.Type() != num.INTEGER_TYPE {
		return &Error{Message: "remainder arguments must be integers"}
	}

	dividendInt := int64(dividend.(num.Integer))
	divisorInt := int64(divisor.(num.Integer))

	if divisorInt == 0 {
		return &Error{Message: "remainder: division by zero"}
	}

	return &obj.NumberObject{Value: num.Integer(dividendInt % divisorInt)}
}

func Modulo(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 2 {
		return &Error{Message: "modulo requires exactly 2 arguments"}
	}

	if args[0].Type() != obj.NUMBER_OBJ || args[1].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "modulo arguments must be numbers"}
	}

	dividend := args[0].(*obj.NumberObject).Value
	divisor := args[1].(*obj.NumberObject).Value

	if dividend.Type() != num.INTEGER_TYPE || divisor.Type() != num.INTEGER_TYPE {
		return &Error{Message: "modulo arguments must be integers"}
	}

	dividendInt := int64(dividend.(num.Integer))
	divisorInt := int64(divisor.(num.Integer))

	if divisorInt == 0 {
		return &Error{Message: "modulo: division by zero"}
	}

	result := dividendInt % divisorInt
	// Ensure the result has the same sign as the divisor (modulo behavior)
	if (result > 0 && divisorInt < 0) || (result < 0 && divisorInt > 0) {
		result += divisorInt
	}

	return &obj.NumberObject{Value: num.Integer(result)}
}

func Gcd(args []obj.Object, env *obj.Environment) obj.Object {
	// GCD with 0 arguments returns 0
	if len(args) == 0 {
		return &obj.NumberObject{Value: num.Integer(0)}
	}

	// GCD with 1 argument returns the absolute value of that argument
	if len(args) == 1 {
		if args[0].Type() != obj.NUMBER_OBJ {
			return &Error{Message: "gcd arguments must be integers"}
		}

		numObj := args[0].(*obj.NumberObject).Value
		if numObj.Type() != num.INTEGER_TYPE {
			return &Error{Message: "gcd arguments must be integers"}
		}

		intVal := int64(numObj.(num.Integer))
		if intVal < 0 {
			intVal = -intVal
		}
		return &obj.NumberObject{Value: num.Integer(intVal)}
	}

	// GCD with multiple arguments
	for _, arg := range args {
		if arg.Type() != obj.NUMBER_OBJ {
			return &Error{Message: "gcd arguments must be integers"}
		}

		numObj := arg.(*obj.NumberObject).Value
		if numObj.Type() != num.INTEGER_TYPE {
			return &Error{Message: "gcd arguments must be integers"}
		}
	}

	// Calculate GCD of all arguments
	result := int64(args[0].(*obj.NumberObject).Value.(num.Integer))
	if result < 0 {
		result = -result
	}

	for _, arg := range args[1:] {
		current := int64(arg.(*obj.NumberObject).Value.(num.Integer))
		if current < 0 {
			current = -current
		}

		// Euclidean algorithm
		for current != 0 {
			temp := current
			current = result % current
			result = temp
		}
	}

	return &obj.NumberObject{Value: num.Integer(result)}
}

func Lcm(args []obj.Object, env *obj.Environment) obj.Object {
	// LCM with 0 arguments returns 1
	if len(args) == 0 {
		return &obj.NumberObject{Value: num.Integer(1)}
	}

	// LCM with 1 argument returns the absolute value of that argument
	if len(args) == 1 {
		if args[0].Type() != obj.NUMBER_OBJ {
			return &Error{Message: "lcm arguments must be integers"}
		}

		numObj := args[0].(*obj.NumberObject).Value
		if numObj.Type() != num.INTEGER_TYPE {
			return &Error{Message: "lcm arguments must be integers"}
		}

		intVal := int64(numObj.(num.Integer))
		if intVal < 0 {
			intVal = -intVal
		}
		return &obj.NumberObject{Value: num.Integer(intVal)}
	}

	// LCM with multiple arguments
	for _, arg := range args {
		if arg.Type() != obj.NUMBER_OBJ {
			return &Error{Message: "lcm arguments must be integers"}
		}

		numObj := arg.(*obj.NumberObject).Value
		if numObj.Type() != num.INTEGER_TYPE {
			return &Error{Message: "lcm arguments must be integers"}
		}
	}

	// Calculate LCM of all arguments
	result := int64(args[0].(*obj.NumberObject).Value.(num.Integer))
	if result < 0 {
		result = -result
	}

	for _, arg := range args[1:] {
		current := int64(arg.(*obj.NumberObject).Value.(num.Integer))
		if current < 0 {
			current = -current
		}

		if result == 0 || current == 0 {
			return &obj.NumberObject{Value: num.Integer(0)}
		}

		// LCM(a,b) = abs(a*b) / GCD(a,b)
		// First calculate GCD
		a, b := result, current
		for b != 0 {
			temp := b
			b = a % b
			a = temp
		}
		gcd := a

		// Then calculate LCM
		result = (result * current) / gcd
	}

	return &obj.NumberObject{Value: num.Integer(result)}
}

func FloorFunc(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "floor requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "floor argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		return &obj.NumberObject{Value: numObj}

	case num.RATIONAL_TYPE:
		rat := numObj.(num.Rational)
		br := (*big.Rat)(&rat)
		n := new(big.Int).Set(br.Num())
		d := new(big.Int).Set(br.Denom())
		// Integer division truncated toward zero
		floorBig := new(big.Int).Quo(n, d)
		// If negative and there is a non-zero remainder, subtract 1 for floor
		rem := new(big.Int).Rem(n, d)
		if n.Sign() < 0 && rem.Sign() != 0 {
			floorBig.Sub(floorBig, big.NewInt(1))
		}
		return &obj.NumberObject{Value: num.Integer(floorBig.Int64())}

	case num.REAL_TYPE:
		realVal := float64(numObj.(num.Real))
		return &obj.NumberObject{Value: num.Integer(int64(math.Floor(realVal)))}
	}

	return &obj.NumberObject{Value: numObj}
}

func CeilingFunc(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "ceiling requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "ceiling argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		return &obj.NumberObject{Value: numObj}

	case num.RATIONAL_TYPE:
		rat := numObj.(num.Rational)
		br := (*big.Rat)(&rat)
		n := new(big.Int).Set(br.Num())
		d := new(big.Int).Set(br.Denom())
		ceilBig := new(big.Int).Quo(n, d)
		rem := new(big.Int).Rem(n, d)
		if n.Sign() > 0 && rem.Sign() != 0 {
			ceilBig.Add(ceilBig, big.NewInt(1))
		}
		return &obj.NumberObject{Value: num.Integer(ceilBig.Int64())}

	case num.REAL_TYPE:
		realVal := float64(numObj.(num.Real))
		return &obj.NumberObject{Value: num.Integer(int64(math.Ceil(realVal)))}
	}

	return &obj.NumberObject{Value: numObj}
}

func TruncateFunc(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "truncate requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "truncate argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		return &obj.NumberObject{Value: numObj}

	case num.RATIONAL_TYPE:
		rat := numObj.(num.Rational)
		br := (*big.Rat)(&rat)
		n := new(big.Int).Set(br.Num())
		d := new(big.Int).Set(br.Denom())
		truncBig := new(big.Int).Quo(n, d)
		return &obj.NumberObject{Value: num.Integer(truncBig.Int64())}

	case num.REAL_TYPE:
		realVal := float64(numObj.(num.Real))
		return &obj.NumberObject{Value: num.Integer(int64(math.Trunc(realVal)))}
	}

	return &obj.NumberObject{Value: numObj}
}

func RoundFunc(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "round requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "round argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		intVal := int64(numObj.(num.Integer))

	case num.RATIONAL_TYPE:
		rat := numObj.(num.Rational)
		br := (*big.Rat)(&rat)
		realVal, _ := new(big.Float).SetRat(br).Float64()
		return &obj.NumberObject{Value: num.Integer(int64(math.Round(realVal)))}

	case num.REAL_TYPE:
		realVal := float64(numObj.(num.Real))
		return &obj.NumberObject{Value: num.Integer(int64(math.Round(realVal)))}
	}

	return &obj.NumberObject{Value: numObj}
}

// floatToRational converts a float to a rational using continued fractions
func floatToRational(f float64) num.Rational {
	// Handle special cases
	if f == 0 {
		return num.Rational(*big.NewRat(0, 1))
	}

	// Handle negative numbers
	negative := false
	if f < 0 {
		negative = true
		f = -f
	}

	// Use a simple approach for now - convert to rational with a fixed denominator
	// This is not optimal but works for basic cases
	// A more sophisticated approach would use continued fractions

	// Scale to make it an integer
	scale := 1.0
	for f != math.Floor(f) && scale < 1e12 {
		f *= 10
		scale *= 10
	}

	numerator := int64(f)
	denominator := int64(scale)

	// Simplify the fraction
	gcd := func(a, b int64) int64 {
		for b != 0 {
			a, b = b, a%b
		}
		return a
	}(numerator, denominator)

	numerator /= gcd
	denominator /= gcd

	if negative {
		numerator = -numerator
	}

	return num.Rational(*big.NewRat(numerator, denominator))
}

// Rationalize returns the simplest rational number within a given tolerance of x
func Rationalize(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 2 {
		return &Error{Message: "rationalize requires exactly 2 arguments"}
	}

	if args[0].Type() != obj.NUMBER_OBJ || args[1].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "rationalize arguments must be numbers"}
	}

	x := args[0].(*obj.NumberObject).Value.(num.RNumber)
	y := args[1].(*obj.NumberObject).Value.(num.RNumber)

	// Handle special cases with infinities and NaNs
	switch x.Type() {
	case num.REAL_TYPE:
		xReal := float64(x.(num.Real))
		if math.IsInf(xReal, 0) {
			// (rationalize +inf.0 3) => 0.0
			// (rationalize +inf.0 +inf.0) => +nan.0
			switch y.Type() {
			case num.REAL_TYPE:
				yReal := float64(y.(num.Real))
				if math.IsInf(yReal, 0) && math.IsInf(xReal, 1) && math.IsInf(yReal, 1) {
					// Both +inf
					return &obj.NumberObject{Value: num.Real(math.Inf(1))}
				} else if math.IsInf(yReal, 0) && math.IsInf(xReal, -1) && math.IsInf(yReal, -1) {
					// Both -inf
					return &obj.NumberObject{Value: num.Real(math.Inf(-1))}
				} else if math.IsInf(yReal, 0) {
					// Both inf, but different signs
					return &obj.NumberObject{Value: num.Real(math.NaN())}
				}
			}
			// (rationalize +inf.0 3) => 0.0 or similar cases
			return &obj.NumberObject{Value: num.Real(0.0)}
		} else if math.IsNaN(xReal) {
			return &obj.NumberObject{Value: num.Real(math.NaN())}
		}
	}

	switch y.Type() {
	case num.REAL_TYPE:
		yReal := float64(y.(num.Real))
		if math.IsInf(yReal, 0) {
			// When tolerance is infinite, return 0
			return &obj.NumberObject{Value: num.Real(0.0)}
		} else if math.IsNaN(yReal) {
			return &obj.NumberObject{Value: num.Real(math.NaN())}
		}
	}

	// Convert both numbers to real for calculation
	var xReal, yReal float64

	switch x.Type() {
	case num.INTEGER_TYPE:
		xReal = float64(x.(num.Integer))
	case num.RATIONAL_TYPE:
		rat := x.(num.Rational)
		br := (*big.Rat)(&rat)
		xr, _ := new(big.Float).SetRat(br).Float64()
		xReal = xr
	case num.REAL_TYPE:
		xReal = float64(x.(num.Real))
	}

	switch y.Type() {
	case num.INTEGER_TYPE:
		yReal = float64(y.(num.Integer))
	case num.RATIONAL_TYPE:
		rat := y.(num.Rational)
		br := (*big.Rat)(&rat)
		yr, _ := new(big.Float).SetRat(br).Float64()
		yReal = yr
	case num.REAL_TYPE:
		yReal = float64(y.(num.Real))
	}

	// Take absolute value of y
	if yReal < 0 {
		yReal = -yReal
	}

	// If y is zero, find the simplest rational equal to x
	if yReal == 0 {
		// Convert RNumber to Number
		var number num.Number
		switch x.Type() {
		case num.INTEGER_TYPE:
			intVal := x.(num.Integer)
			number = intVal
		case num.RATIONAL_TYPE:
			ratVal := x.(num.Rational)
			number = ratVal
		case num.REAL_TYPE:
			realVal := x.(num.Real)
			number = realVal
		}
		return &obj.NumberObject{Value: number}
	}

	// Find the simplest fraction within [x-y, x+y]
	lowerBound := xReal - yReal
	upperBound := xReal + yReal

	// Handle case when interval includes 0
	if lowerBound <= 0 && upperBound >= 0 {
		return &obj.NumberObject{Value: num.Integer{Value: 0}}
	}

	// Find the simplest rational in the range
	result := findSimplestRational(lowerBound, upperBound, xReal)
	return &obj.NumberObject{Value: result}
}

// findSimplestRational finds the simplest rational number in the given range [lower, upper]
// A rational p/q in lowest terms is simpler than p'/q' if |p| <= |p'| and |q| <= |q|, with at least one inequality being strict
func findSimplestRational(lower, upper, target float64) num.Number {
	// Handle special cases
	if lower <= 0 && upper >= 0 {
		return num.Integer{Value: 0}
	}

	// Ensure lower < upper
	if lower > upper {
		lower, upper = upper, lower
	}

	// First, check if the target itself is a simple rational that fits
	// Check if target is an integer
	if target == float64(int64(target)) {
		intTarget := int64(target)
		// Check if it's within bounds
		if float64(intTarget) >= lower && float64(intTarget) <= upper {
			return num.Integer{Value: intTarget}
		}
	}

	// Try to find the simplest fraction using continued fractions approach
	// Start with 0/1 and 1/1, then generate mediants
	n1, d1 := int64(0), int64(1) // First fraction 0/1
	n2, d2 := int64(1), int64(1) // Second fraction 1/1

	// If both bounds are negative, work with positive values and negate at the end
	negate := false
	if upper < 0 {
		lower, upper = -upper, -lower
		negate = true
		target = -target
	}

	// Check if 0/1 or 1/1 is within range
	if float64(n1)/float64(d1) >= lower && float64(n1)/float64(d1) <= upper {
		if negate {
			n1 = -n1
		}
		return num.Integer(n1)
	}

	if float64(n2)/float64(d2) >= lower && float64(n2)/float64(d2) <= upper {
		if negate {
			n2 = -n2
		}
		return num.Integer(n2)
	}

	for {
		// Calculate mediant (n1+n2)/(d1+d2)
		n := n1 + n2
		d := d1 + d2

		// Check if mediant is in range
		val := float64(n) / float64(d)

		if val < lower {
			// Move first fraction
			n1, d1 = n, d
		} else if val > upper {
			// Move second fraction
			n2, d2 = n, d
		} else {
			// Found a fraction in range
			if negate {
				n = -n
			}
			// Return the fraction in lowest terms
			// Simplify the fraction using GCD
			gcd := func(a, b int64) int64 {
				if a < 0 {
					a = -a
				}
				if b < 0 {
					b = -b
				}
				for b != 0 {
					a, b = b, a%b
				}
				return a
			}(n, d)

			return num.Rational(*big.NewRat(n/gcd, d/gcd))
		}

		// Prevent overflow
		if d > 1000000 {
			break
		}
	}

	// Fallback: convert the target to a rational if it's within bounds
	if target >= lower && target <= upper {
		result := floatToRational(target)
		if negate {
			br := (*big.Rat)(&result)
			br.Neg(br)
		}
		return result
	}

	// Fallback to midpoint
	midpoint := (lower + upper) / 2
	result := floatToRational(midpoint)
	if negate {
		br := (*big.Rat)(&result)
		br.Neg(br)
	}

	return result
}

// Asin computes the arc sine of a number
func Asin(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "asin requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "asin argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		intVal := int64(numObj.(num.Integer))
		if intVal < -1 || intVal > 1 {
			// For values outside [-1, 1], result is complex
			// Simplified approach: return NaN for now
			return &obj.NumberObject{Value: num.Real(math.NaN())}
		}
		result := math.Asin(float64(intVal))
		return &obj.NumberObject{Value: num.Real(result)}

	case num.RATIONAL_TYPE:
		rat := numObj.(num.Rational)
		br := (*big.Rat)(&rat)
		floatVal, _ := new(big.Float).SetRat(br).Float64()
		if floatVal < -1 || floatVal > 1 {
			// For values outside [-1, 1], result is complex
			return &obj.NumberObject{Value: num.Real(math.NaN())}
		}
		result := math.Asin(floatVal)
		return &obj.NumberObject{Value: num.Real(result)}

	case num.REAL_TYPE:
		realVal := float64(numObj.(num.Real))
		if realVal < -1 || realVal > 1 {
			// For values outside [-1, 1], result is complex
			return &obj.NumberObject{Value: num.Real(math.NaN())}
		}
		result := math.Asin(realVal)
		return &obj.NumberObject{Value: num.Real(result)}

	case num.COMPLEX_TYPE:
		// For complex asin(z) = -i * ln(iz + sqrt(1-z^2))
		complexVal := numObj.(num.Complex)

		// Convert real and imaginary parts to float64
		var realPart, imagPart float64

		switch complexVal.RealPart.Type() {
		case num.INTEGER_TYPE:
			realPart = float64(complexVal.RealPart.(num.Integer))
		case num.RATIONAL_TYPE:
			rat := complexVal.RealPart.(num.Rational)
			br := (*big.Rat)(&rat)
			rv, _ := new(big.Float).SetRat(br).Float64()
			realPart = rv
		case num.REAL_TYPE:
			realPart = float64(complexVal.RealPart.(num.Real))
		}

		switch complexVal.ImagPart.Type() {
		case num.INTEGER_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Integer))
		case num.RATIONAL_TYPE:
			rat := complexVal.ImagPart.(num.Rational)
			br := (*big.Rat)(&rat)
			iv, _ := new(big.Float).SetRat(br).Float64()
			imagPart = iv
		case num.REAL_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Real))
		}

		// Calculate asin using complex formula
		// asin(z) = -i * ln(iz + sqrt(1-z^2))
		z := complex(realPart, imagPart)
		result := cmplx.Asin(z)

		return &obj.NumberObject{Value: num.Complex{
			RealPart: num.Real(real(result)),
			ImagPart: num.Real(imag(result)),
		}}
	}

	return &Error{Message: "asin: unsupported number type"}
}

// Acos computes the arc cosine of a number
func Acos(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "acos requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "acos argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		intVal := int64(numObj.(num.Integer))
		if intVal < -1 || intVal > 1 {
			// For values outside [-1, 1], result is complex
			return &obj.NumberObject{Value: num.Real(math.NaN())}
		}
		result := math.Acos(float64(intVal))
		return &obj.NumberObject{Value: num.Real(result)}

	case num.RATIONAL_TYPE:
		rat := numObj.(num.Rational)
		br := (*big.Rat)(&rat)
		floatVal, _ := new(big.Float).SetRat(br).Float64()
		if floatVal < -1 || floatVal > 1 {
			// For values outside [-1, 1], result is complex
			return &obj.NumberObject{Value: num.Real(math.NaN())}
		}
		result := math.Acos(floatVal)
		return &obj.NumberObject{Value: num.Real(result)}

	case num.REAL_TYPE:
		realVal := float64(numObj.(num.Real))
		if realVal < -1 || realVal > 1 {
			// For values outside [-1, 1], result is complex
			return &obj.NumberObject{Value: num.Real(math.NaN())}
		}
		result := math.Acos(realVal)
		return &obj.NumberObject{Value: num.Real(result)}

	case num.COMPLEX_TYPE:
		// For complex acos(z) = pi/2 - asin(z)
		complexVal := numObj.(num.Complex)

		// Convert real and imaginary parts to float64
		var realPart, imagPart float64

		switch complexVal.RealPart.Type() {
		case num.INTEGER_TYPE:
			realPart = float64(complexVal.RealPart.(num.Integer))
		case num.RATIONAL_TYPE:
			rat := complexVal.RealPart.(num.Rational)
			br := (*big.Rat)(&rat)
			rv, _ := new(big.Float).SetRat(br).Float64()
			realPart = rv
		case num.REAL_TYPE:
			realPart = float64(complexVal.RealPart.(num.Real))
		}

		switch complexVal.ImagPart.Type() {
		case num.INTEGER_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Integer))
		case num.RATIONAL_TYPE:
			rat := complexVal.ImagPart.(num.Rational)
			br := (*big.Rat)(&rat)
			iv, _ := new(big.Float).SetRat(br).Float64()
			imagPart = iv
		case num.REAL_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Real))
		}

		// Calculate acos using complex formula
		z := complex(realPart, imagPart)
		result := cmplx.Acos(z)

		return &obj.NumberObject{Value: num.Complex{
			RealPart: num.Real(real(result)),
			ImagPart: num.Real(imag(result)),
		}}
	}

	return &Error{Message: "acos: unsupported number type"}
}

// Atan computes the arc tangent of a number
func Atan(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) < 1 || len(args) > 2 {
		return &Error{Message: "atan requires 1 or 2 arguments"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "atan argument must be a number"}
	}

	// Two-argument atan (atan y x)
	if len(args) == 2 {
		if args[1].Type() != obj.NUMBER_OBJ {
			return &Error{Message: "atan second argument must be a number"}
		}

		yObj := args[0].(*obj.NumberObject).Value
		xObj := args[1].(*obj.NumberObject).Value

		// Convert both arguments to float64
		var y, x float64

		switch yObj.Type() {
		case num.INTEGER_TYPE:
			y = float64(yObj.(num.Integer))
		case num.RATIONAL_TYPE:
			rat := yObj.(num.Rational)
			br := (*big.Rat)(&rat)
			vv, _ := new(big.Float).SetRat(br).Float64()
			y = vv
		case num.REAL_TYPE:
			y = float64(yObj.(num.Real))
		case num.COMPLEX_TYPE:
			return &Error{Message: "atan: complex arguments not supported for two-argument form"}
		}

		switch xObj.Type() {
		case num.INTEGER_TYPE:
			x = float64(xObj.(num.Integer))
		case num.RATIONAL_TYPE:
			rat := xObj.(num.Rational)
			br := (*big.Rat)(&rat)
			vv, _ := new(big.Float).SetRat(br).Float64()
			x = vv
		case num.REAL_TYPE:
			x = float64(xObj.(num.Real))
		case num.COMPLEX_TYPE:
			return &Error{Message: "atan: complex arguments not supported for two-argument form"}
		}

		result := math.Atan2(y, x)
		return &obj.NumberObject{Value: num.Real(result)}
	}

	// Single-argument atan
	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		intVal := int64(numObj.(num.Integer))
		result := math.Atan(float64(intVal))
		return &obj.NumberObject{Value: num.Real(result)}

	case num.RATIONAL_TYPE:
		rat := numObj.(num.Rational)
		br := (*big.Rat)(&rat)
		floatVal, _ := new(big.Float).SetRat(br).Float64()
		result := math.Atan(floatVal)
		return &obj.NumberObject{Value: num.Real(result)}

	case num.REAL_TYPE:
		realVal := float64(numObj.(num.Real))
		result := math.Atan(realVal)
		return &obj.NumberObject{Value: num.Real(result)}

	case num.COMPLEX_TYPE:
		// For complex atan(z) = (i/2) * ln((i+z)/(i-z))
		complexVal := numObj.(num.Complex)

		// Convert real and imaginary parts to float64
		var realPart, imagPart float64

		switch complexVal.RealPart.Type() {
		case num.INTEGER_TYPE:
			realPart = float64(complexVal.RealPart.(num.Integer))
		case num.RATIONAL_TYPE:
			rat := complexVal.RealPart.(num.Rational)
			br := (*big.Rat)(&rat)
			rv, _ := new(big.Float).SetRat(br).Float64()
			realPart = rv
		case num.REAL_TYPE:
			realPart = float64(complexVal.RealPart.(num.Real))
		}

		switch complexVal.ImagPart.Type() {
		case num.INTEGER_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Integer))
		case num.RATIONAL_TYPE:
			rat := complexVal.ImagPart.(num.Rational)
			br := (*big.Rat)(&rat)
			iv, _ := new(big.Float).SetRat(br).Float64()
			imagPart = iv
		case num.REAL_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Real))
		}

		// Calculate atan using complex formula
		z := complex(realPart, imagPart)
		result := cmplx.Atan(z)

		return &obj.NumberObject{Value: num.Complex{
			RealPart: num.Real(real(result)),
			ImagPart: num.Real(imag(result)),
		}}
	}

	return &Error{Message: "atan: unsupported number type"}
}

// Tan returns the tangent of a number
func Tan(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "tan requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "tan argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		intVal := int64(numObj.(num.Integer))
		result := math.Tan(float64(intVal))
		return &obj.NumberObject{Value: num.Real(result)}

	case num.RATIONAL_TYPE:
		ratVal := numObj.(num.Rational)
		br := (*big.Rat)(&ratVal)
		floatVal, _ := new(big.Float).SetRat(br).Float64()
		result := math.Tan(floatVal)
		return &obj.NumberObject{Value: num.Real(result)}

	case num.REAL_TYPE:
		realVal := float64(numObj.(num.Real))
		result := math.Tan(realVal)
		return &obj.NumberObject{Value: num.Real(result)}

	case num.COMPLEX_TYPE:
		// For complex numbers z = a + bi, tan(z) = sin(2a)/(cos(2a)+cosh(2b)) + i*sinh(2b)/(cos(2a)+cosh(2b))
		complexVal := numObj.(num.Complex)

		// Convert real and imaginary parts to float64
		var realPart, imagPart float64

		switch complexVal.RealPart.Type() {
		case num.INTEGER_TYPE:
			realPart = float64(complexVal.RealPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.RealPart.(num.Rational)
			realPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			realPart = complexVal.RealPart.(num.Real).Value
		}

		switch complexVal.ImagPart.Type() {
		case num.INTEGER_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.ImagPart.(num.Rational)
			imagPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			imagPart = complexVal.ImagPart.(num.Real).Value
		}

		// Calculate denominator = cos(2a) + cosh(2b)
		denom := math.Cos(2*realPart) + math.Cosh(2*imagPart)

		// Avoid division by zero
		if denom == 0 {
			return &Error{Message: "tan: division by zero in complex tangent"}
		}

		// Calculate real part = sin(2a)/denom
		realResult := math.Sin(2*realPart) / denom

		// Calculate imaginary part = sinh(2b)/denom
		imagResult := math.Sinh(2*imagPart) / denom

		// If imaginary part is zero, return real number
		if imagResult == 0 {
			return &obj.NumberObject{Value: num.Real(realResult)}
		}

		return &obj.NumberObject{Value: num.Complex{
			RealPart: num.Real(realResult),
			ImagPart: num.Real(imagResult),
		}}
	}

	return &Error{Message: "tan: unsupported number type"}
}

// Sqrt returns the square root of a number
func Sqrt(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "sqrt requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "sqrt argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		intVal := int64(numObj.(num.Integer))
		if intVal < 0 {
			// For negative integers, result is complex
			floatVal := math.Sqrt(float64(-intVal))
			return &obj.NumberObject{Value: num.Complex{
				RealPart: num.Real(0.0),
				ImagPart: num.Real(floatVal),
			}}
		}
		// For non-negative integers, check if result is integer
		sqrtVal := math.Sqrt(float64(intVal))
		if sqrtVal == float64(int64(sqrtVal)) {
			// Perfect square
			return &obj.NumberObject{Value: num.Integer(int64(sqrtVal))}
		}
		// Return as real
		return &obj.NumberObject{Value: num.Real(sqrtVal)}

	case num.RATIONAL_TYPE:
		ratVal := numObj.(num.Rational)
		br := (*big.Rat)(&ratVal)
		floatVal, _ := new(big.Float).SetRat(br).Float64()
		floatVal = math.Sqrt(floatVal)
		// Return as real (no attempt to detect perfect square rational yet)
		return &obj.NumberObject{Value: num.Real(floatVal)}

	case num.REAL_TYPE:
		realVal := float64(numObj.(num.Real))
		if realVal < 0 {
			// For negative reals, result is complex
			sqrtVal := math.Sqrt(-realVal)
			return &obj.NumberObject{Value: num.Complex{
				RealPart: num.Real(0.0),
				ImagPart: num.Real(sqrtVal),
			}}
		}
		result := math.Sqrt(realVal)
		return &obj.NumberObject{Value: num.Real{Value: result}}

	case num.COMPLEX_TYPE:
		// For complex numbers z = a + bi, sqrt(z) = sqrt((|z|+a)/2) + i*sign(b)*sqrt((|z|-a)/2)
		// where |z| = sqrt(a² + b²)
		complexVal := numObj.(num.Complex)

		// Convert real and imaginary parts to float64
		var realPart, imagPart float64

		switch complexVal.RealPart.Type() {
		case num.INTEGER_TYPE:
			realPart = float64(complexVal.RealPart.(num.Integer))
		case num.RATIONAL_TYPE:
			rat := complexVal.RealPart.(num.Rational)
			br := (*big.Rat)(&rat)
			rv, _ := new(big.Float).SetRat(br).Float64()
			realPart = rv
		case num.REAL_TYPE:
			realPart = float64(complexVal.RealPart.(num.Real))
		}

		switch complexVal.ImagPart.Type() {
		case num.INTEGER_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Integer))
		case num.RATIONAL_TYPE:
			rat := complexVal.ImagPart.(num.Rational)
			br := (*big.Rat)(&rat)
			iv, _ := new(big.Float).SetRat(br).Float64()
			imagPart = iv
		case num.REAL_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Real))
		}

		// Calculate |z| = sqrt(a² + b²)
		modulus := math.Sqrt(realPart*realPart + imagPart*imagPart)

		// Calculate the real and imaginary parts of the square root
		sqrtReal := math.Sqrt((modulus + realPart) / 2)
		sqrtImag := math.Sqrt((modulus - realPart) / 2)

		// Adjust sign of imaginary part
		if imagPart < 0 {
			sqrtImag = -sqrtImag
		}

		// If imaginary part is zero, return real number
		if sqrtImag == 0 {
			// Check if result should be integer or rational
			if sqrtReal == float64(int64(sqrtReal)) {
				return &obj.NumberObject{Value: num.Integer(int64(sqrtReal))}
			}
			return &obj.NumberObject{Value: num.Real(sqrtReal)}
		}

		return &obj.NumberObject{Value: num.Complex{
			RealPart: num.Real(sqrtReal),
			ImagPart: num.Real(sqrtImag),
		}}
	}

	return &Error{Message: "sqrt: unsupported number type"}
}

// Expt returns z1 raised to the power of z2
func Expt(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 2 {
		return &Error{Message: "expt requires exactly 2 arguments"}
	}

	if args[0].Type() != obj.NUMBER_OBJ || args[1].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "expt arguments must be numbers"}
	}

	base := args[0].(*obj.NumberObject).Value
	exponent := args[1].(*obj.NumberObject).Value

	// Handle special cases
	switch base.Type() {
	case num.INTEGER_TYPE:
		baseInt := base.(num.Integer).Value
		if baseInt == 0 {
			// 0^z
			switch exponent.Type() {
			case num.INTEGER_TYPE:
				expInt := exponent.(num.Integer).Value
				if expInt == 0 {
					// 0^0 = 1
					return &obj.NumberObject{Value: num.Integer{Value: 1}}
				} else if expInt > 0 {
					// 0^positive = 0
					return &obj.NumberObject{Value: num.Integer{Value: 0}}
				} else {
					// 0^negative = undefined (infinity)
					return &obj.NumberObject{Value: num.Real{Value: math.Inf(1)}}
				}
			case num.RATIONAL_TYPE:
				expRat := exponent.(num.Rational)
				if expRat.Numerator == 0 {
					// 0^0 = 1
					return &obj.NumberObject{Value: num.Integer{Value: 1}}
				} else if expRat.Numerator > 0 {
					// 0^positive = 0
					return &obj.NumberObject{Value: num.Integer{Value: 0}}
				} else {
					// 0^negative = undefined (infinity)
					return &obj.NumberObject{Value: num.Real{Value: math.Inf(1)}}
				}
			case num.REAL_TYPE:
				expReal := exponent.(num.Real).Value
				if expReal == 0 {
					// 0^0 = 1
					return &obj.NumberObject{Value: num.Integer{Value: 1}}
				} else if expReal > 0 {
					// 0^positive = 0
					return &obj.NumberObject{Value: num.Integer{Value: 0}}
				} else {
					// 0^negative = undefined (infinity)
					return &obj.NumberObject{Value: num.Real{Value: math.Inf(1)}}
				}
			}
		} else if baseInt == 1 {
			// 1^z = 1 for any z
			return &obj.NumberObject{Value: num.Integer{Value: 1}}
		} else if baseInt == -1 {
			// (-1)^z
			switch exponent.Type() {
			case num.INTEGER_TYPE:
				expInt := exponent.(num.Integer).Value
				if expInt%2 == 0 {
					// (-1)^(even) = 1
					return &obj.NumberObject{Value: num.Integer{Value: 1}}
				} else {
					// (-1)^(odd) = -1
					return &obj.NumberObject{Value: num.Integer{Value: -1}}
				}
			}
		} else {
			// Handle other integer bases with integer exponents using integer arithmetic
			if exponent.Type() == num.INTEGER_TYPE {
				expInt := exponent.(num.Integer).Value
				if expInt >= 0 && expInt <= 62 { // Avoid overflow, 2^62 is still safe for int64
					// Use integer exponentiation
					result := int64(1)
					baseVal := baseInt
					if baseVal < 0 && expInt%2 == 1 {
						// Negative base with odd exponent
						baseVal = -baseVal
						for i := int64(0); i < expInt; i++ {
							result *= baseVal
						}
						result = -result
					} else {
						// Positive base or negative base with even exponent
						if baseVal < 0 {
							baseVal = -baseVal
						}
						for i := int64(0); i < expInt; i++ {
							result *= baseVal
						}
						// If base was negative and exponent even, result is positive (already handled)
					}
					return &obj.NumberObject{Value: num.Integer{Value: result}}
				}
			}
		}
	case num.RATIONAL_TYPE:
		baseRat := base.(num.Rational)
		if baseRat.Numerator == 0 {
			// 0^z cases (same as above)
			switch exponent.Type() {
			case num.INTEGER_TYPE:
				expInt := exponent.(num.Integer).Value
				if expInt == 0 {
					return &obj.NumberObject{Value: num.Integer{Value: 1}}
				} else if expInt > 0 {
					return &obj.NumberObject{Value: num.Integer{Value: 0}}
				} else {
					return &obj.NumberObject{Value: num.Real{Value: math.Inf(1)}}
				}
			case num.RATIONAL_TYPE:
				expRat := exponent.(num.Rational)
				if expRat.Numerator == 0 {
					return &obj.NumberObject{Value: num.Integer{Value: 1}}
				} else if expRat.Numerator > 0 {
					return &obj.NumberObject{Value: num.Integer{Value: 0}}
				} else {
					return &obj.NumberObject{Value: num.Real{Value: math.Inf(1)}}
				}
			case num.REAL_TYPE:
				expReal := exponent.(num.Real).Value
				if expReal == 0 {
					return &obj.NumberObject{Value: num.Integer{Value: 1}}
				} else if expReal > 0 {
					return &obj.NumberObject{Value: num.Integer{Value: 0}}
				} else {
					return &obj.NumberObject{Value: num.Real{Value: math.Inf(1)}}
				}
			}
		}
	case num.REAL_TYPE:
		baseReal := base.(num.Real).Value
		if baseReal == 0.0 {
			// 0^z cases (same as above)
			switch exponent.Type() {
			case num.INTEGER_TYPE:
				expInt := exponent.(num.Integer).Value
				if expInt == 0 {
					return &obj.NumberObject{Value: num.Integer{Value: 1}}
				} else if expInt > 0 {
					return &obj.NumberObject{Value: num.Integer{Value: 0}}
				} else {
					return &obj.NumberObject{Value: num.Real{Value: math.Inf(1)}}
				}
			case num.RATIONAL_TYPE:
				expRat := exponent.(num.Rational)
				if expRat.Numerator == 0 {
					return &obj.NumberObject{Value: num.Integer{Value: 1}}
				} else if expRat.Numerator > 0 {
					return &obj.NumberObject{Value: num.Integer{Value: 0}}
				} else {
					return &obj.NumberObject{Value: num.Real{Value: math.Inf(1)}}
				}
			case num.REAL_TYPE:
				expReal := exponent.(num.Real).Value
				if expReal == 0 {
					return &obj.NumberObject{Value: num.Integer{Value: 1}}
				} else if expReal > 0 {
					return &obj.NumberObject{Value: num.Integer{Value: 0}}
				} else {
					return &obj.NumberObject{Value: num.Real{Value: math.Inf(1)}}
				}
			}
		} else if baseReal == 1.0 {
			// 1^z = 1 for any z
			return &obj.NumberObject{Value: num.Integer{Value: 1}}
		}
	}

	// General case: use cmplx.Pow for complex numbers or math.Pow for reals
	var baseComplex, expComplex complex128

	// Convert base to complex
	switch base.Type() {
	case num.INTEGER_TYPE:
		baseInt := base.(num.Integer).Value
		baseComplex = complex(float64(baseInt), 0)
	case num.RATIONAL_TYPE:
		baseRat := base.(num.Rational)
		baseFloat := float64(baseRat.Numerator) / float64(baseRat.Denominator)
		baseComplex = complex(baseFloat, 0)
	case num.REAL_TYPE:
		baseReal := base.(num.Real).Value
		baseComplex = complex(baseReal, 0)
	case num.COMPLEX_TYPE:
		complexVal := base.(num.Complex)
		var realPart, imagPart float64
		switch complexVal.RealPart.Type() {
		case num.INTEGER_TYPE:
			realPart = float64(complexVal.RealPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.RealPart.(num.Rational)
			realPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			realPart = complexVal.RealPart.(num.Real).Value
		}
		switch complexVal.ImagPart.Type() {
		case num.INTEGER_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.ImagPart.(num.Rational)
			imagPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			imagPart = complexVal.ImagPart.(num.Real).Value
		}
		baseComplex = complex(realPart, imagPart)
	}

	// Convert exponent to complex
	switch exponent.Type() {
	case num.INTEGER_TYPE:
		expInt := exponent.(num.Integer).Value
		expComplex = complex(float64(expInt), 0)
	case num.RATIONAL_TYPE:
		expRat := exponent.(num.Rational)
		expFloat := float64(expRat.Numerator) / float64(expRat.Denominator)
		expComplex = complex(expFloat, 0)
	case num.REAL_TYPE:
		expReal := exponent.(num.Real).Value
		expComplex = complex(expReal, 0)
	case num.COMPLEX_TYPE:
		complexVal := exponent.(num.Complex)
		var realPart, imagPart float64
		switch complexVal.RealPart.Type() {
		case num.INTEGER_TYPE:
			realPart = float64(complexVal.RealPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.RealPart.(num.Rational)
			realPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			realPart = complexVal.RealPart.(num.Real).Value
		}
		switch complexVal.ImagPart.Type() {
		case num.INTEGER_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.ImagPart.(num.Rational)
			imagPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			imagPart = complexVal.ImagPart.(num.Real).Value
		}
		expComplex = complex(realPart, imagPart)
	}

	// Calculate result using complex power function
	result := cmplx.Pow(baseComplex, expComplex)

	// Check if result is real
	if imag(result) == 0 {
		realResult := real(result)
		// Check if it's an integer
		if realResult == float64(int64(realResult)) {
			return &obj.NumberObject{Value: num.Integer{Value: int64(realResult)}}
		}
		// Check if it should be a rational
		return &obj.NumberObject{Value: num.Real{Value: realResult}}
	}

	// Return as complex number
	return &obj.NumberObject{Value: num.Complex{
		RealPart: num.Real{Value: real(result)},
		ImagPart: num.Real{Value: imag(result)},
	}}
}

// MakeRectangular creates a complex number from rectangular coordinates
func MakeRectangular(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 2 {
		return &Error{Message: "make-rectangular requires exactly 2 arguments"}
	}

	if args[0].Type() != obj.NUMBER_OBJ || args[1].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "make-rectangular arguments must be numbers"}
	}

	realObj := args[0].(*obj.NumberObject).Value
	imagObj := args[1].(*obj.NumberObject).Value

	// Convert real part to RNumber
	var realPart num.RNumber
	switch realObj.Type() {
	case num.INTEGER_TYPE:
		realPart = realObj.(num.Integer)
	case num.RATIONAL_TYPE:
		realPart = realObj.(num.Rational)
	case num.REAL_TYPE:
		realPart = realObj.(num.Real)
	default:
		return &Error{Message: "make-rectangular: real part must be real number"}
	}

	// Convert imaginary part to RNumber
	var imagPart num.RNumber
	switch imagObj.Type() {
	case num.INTEGER_TYPE:
		imagPart = imagObj.(num.Integer)
	case num.RATIONAL_TYPE:
		imagPart = imagObj.(num.Rational)
	case num.REAL_TYPE:
		imagPart = imagObj.(num.Real)
	default:
		return &Error{Message: "make-rectangular: imaginary part must be real number"}
	}

	// Create complex number
	complexNum := num.Complex{
		RealPart: realPart,
		ImagPart: imagPart,
	}

	return &obj.NumberObject{Value: complexNum}
}

// MakePolar creates a complex number from polar coordinates
func MakePolar(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 2 {
		return &Error{Message: "make-polar requires exactly 2 arguments"}
	}

	if args[0].Type() != obj.NUMBER_OBJ || args[1].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "make-polar arguments must be numbers"}
	}

	magObj := args[0].(*obj.NumberObject).Value
	angleObj := args[1].(*obj.NumberObject).Value

	// Convert magnitude to float64
	var mag float64
	switch magObj.Type() {
	case num.INTEGER_TYPE:
		mag = float64(magObj.(num.Integer).Value)
	case num.RATIONAL_TYPE:
		rat := magObj.(num.Rational)
		mag = float64(rat.Numerator) / float64(rat.Denominator)
	case num.REAL_TYPE:
		mag = magObj.(num.Real).Value
	default:
		return &Error{Message: "make-polar: magnitude must be real number"}
	}

	// Convert angle to float64
	var angle float64
	switch angleObj.Type() {
	case num.INTEGER_TYPE:
		angle = float64(angleObj.(num.Integer).Value)
	case num.RATIONAL_TYPE:
		rat := angleObj.(num.Rational)
		angle = float64(rat.Numerator) / float64(rat.Denominator)
	case num.REAL_TYPE:
		angle = angleObj.(num.Real).Value
	default:
		return &Error{Message: "make-polar: angle must be real number"}
	}

	// Calculate rectangular coordinates
	realPart := mag * math.Cos(angle)
	imagPart := mag * math.Sin(angle)

	// Create complex number with real parts
	complexNum := num.Complex{
		RealPart: num.Real{Value: realPart},
		ImagPart: num.Real{Value: imagPart},
	}

	return &obj.NumberObject{Value: complexNum}
}

// RealPart returns the real part of a number
func RealPart(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "real-part requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "real-part argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		// Real number, return as is
		return &obj.NumberObject{Value: numObj}
	case num.RATIONAL_TYPE:
		// Real number, return as is
		return &obj.NumberObject{Value: numObj}
	case num.REAL_TYPE:
		// Real number, return as is
		return &obj.NumberObject{Value: numObj}
	case num.COMPLEX_TYPE:
		// Complex number, return real part
		complexVal := numObj.(num.Complex)
		return &obj.NumberObject{Value: complexVal.RealPart.(num.Number)}
	}

	return &Error{Message: "real-part: unsupported number type"}
}

// ImagPart returns the imaginary part of a number
func ImagPart(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "imag-part requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "imag-part argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		// Real number, imaginary part is 0
		return &obj.NumberObject{Value: num.Integer{Value: 0}}
	case num.RATIONAL_TYPE:
		// Real number, imaginary part is 0
		return &obj.NumberObject{Value: num.Integer{Value: 0}}
	case num.REAL_TYPE:
		// Real number, imaginary part is 0
		return &obj.NumberObject{Value: num.Integer{Value: 0}}
	case num.COMPLEX_TYPE:
		// Complex number, return imaginary part
		complexVal := numObj.(num.Complex)
		return &obj.NumberObject{Value: complexVal.ImagPart.(num.Number)}
	}

	return &Error{Message: "imag-part: unsupported number type"}
}

// Magnitude returns the magnitude of a complex number
func Magnitude(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "magnitude requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "magnitude argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		// For real numbers, magnitude is absolute value
		intVal := numObj.(num.Integer).Value
		if intVal < 0 {
			intVal = -intVal
		}
		return &obj.NumberObject{Value: num.Integer{Value: intVal}}

	case num.RATIONAL_TYPE:
		// For real numbers, magnitude is absolute value
		rat := numObj.(num.Rational)
		if rat.Numerator < 0 {
			rat.Numerator = -rat.Numerator
		}
		return &obj.NumberObject{Value: rat}

	case num.REAL_TYPE:
		// For real numbers, magnitude is absolute value
		realVal := numObj.(num.Real).Value
		if realVal < 0 {
			realVal = -realVal
		}
		return &obj.NumberObject{Value: num.Real{Value: realVal}}

	case num.COMPLEX_TYPE:
		// For complex numbers, magnitude is sqrt(real^2 + imag^2)
		complexVal := numObj.(num.Complex)

		// Convert real and imaginary parts to float64
		var realPart, imagPart float64

		switch complexVal.RealPart.Type() {
		case num.INTEGER_TYPE:
			realPart = float64(complexVal.RealPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.RealPart.(num.Rational)
			realPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			realPart = complexVal.RealPart.(num.Real).Value
		}

		switch complexVal.ImagPart.Type() {
		case num.INTEGER_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.ImagPart.(num.Rational)
			imagPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			imagPart = complexVal.ImagPart.(num.Real).Value
		}

		// Calculate magnitude
		mag := math.Sqrt(realPart*realPart + imagPart*imagPart)
		return &obj.NumberObject{Value: num.Real{Value: mag}}
	}

	return &Error{Message: "magnitude: unsupported number type"}
}

// Angle returns the angle of a complex number
func Angle(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "angle requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "angle argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		// For real numbers, angle is 0 if positive, pi if negative
		intVal := numObj.(num.Integer).Value
		if intVal < 0 {
			return &obj.NumberObject{Value: num.Real{Value: math.Pi}}
		}
		return &obj.NumberObject{Value: num.Integer{Value: 0}}

	case num.RATIONAL_TYPE:
		// For real numbers, angle is 0 if positive, pi if negative
		rat := numObj.(num.Rational)
		if rat.Numerator < 0 {
			return &obj.NumberObject{Value: num.Real{Value: math.Pi}}
		}
		return &obj.NumberObject{Value: num.Integer{Value: 0}}

	case num.REAL_TYPE:
		// For real numbers, angle is 0 if positive, pi if negative
		realVal := numObj.(num.Real).Value
		if realVal < 0 {
			return &obj.NumberObject{Value: num.Real{Value: math.Pi}}
		} else if realVal == 0 {
			return &obj.NumberObject{Value: num.Integer{Value: 0}}
		}
		return &obj.NumberObject{Value: num.Integer{Value: 0}}

	case num.COMPLEX_TYPE:
		// For complex numbers, angle is atan2(imag, real)
		complexVal := numObj.(num.Complex)

		// Convert real and imaginary parts to float64
		var realPart, imagPart float64

		switch complexVal.RealPart.Type() {
		case num.INTEGER_TYPE:
			realPart = float64(complexVal.RealPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.RealPart.(num.Rational)
			realPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			realPart = complexVal.RealPart.(num.Real).Value
		}

		switch complexVal.ImagPart.Type() {
		case num.INTEGER_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.ImagPart.(num.Rational)
			imagPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			imagPart = complexVal.ImagPart.(num.Real).Value
		}

		// Calculate angle
		angle := math.Atan2(imagPart, realPart)
		return &obj.NumberObject{Value: num.Real{Value: angle}}
	}

	return &Error{Message: "angle: unsupported number type"}
}

// ExactToInexact converts an exact number to an inexact number
func ExactToInexact(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "exact->inexact requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "exact->inexact argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		// Convert integer to real
		intVal := numObj.(num.Integer).Value
		return &obj.NumberObject{Value: num.Real{Value: float64(intVal)}}

	case num.RATIONAL_TYPE:
		// Convert rational to real
		rat := numObj.(num.Rational)
		realVal := float64(rat.Numerator) / float64(rat.Denominator)
		return &obj.NumberObject{Value: num.Real{Value: realVal}}

	case num.REAL_TYPE:
		// Already inexact, return as is
		return &obj.NumberObject{Value: numObj}

	case num.COMPLEX_TYPE:
		// Convert complex with exact parts to inexact parts
		complexVal := numObj.(num.Complex)

		// Convert real part
		var realPart num.RNumber
		switch complexVal.RealPart.Type() {
		case num.INTEGER_TYPE:
			intVal := complexVal.RealPart.(num.Integer).Value
			realPart = num.Real{Value: float64(intVal)}
		case num.RATIONAL_TYPE:
			rat := complexVal.RealPart.(num.Rational)
			realVal := float64(rat.Numerator) / float64(rat.Denominator)
			realPart = num.Real{Value: realVal}
		case num.REAL_TYPE:
			realPart = complexVal.RealPart
		}

		// Convert imaginary part
		var imagPart num.RNumber
		switch complexVal.ImagPart.Type() {
		case num.INTEGER_TYPE:
			intVal := complexVal.ImagPart.(num.Integer).Value
			imagPart = num.Real{Value: float64(intVal)}
		case num.RATIONAL_TYPE:
			rat := complexVal.ImagPart.(num.Rational)
			realVal := float64(rat.Numerator) / float64(rat.Denominator)
			imagPart = num.Real{Value: realVal}
		case num.REAL_TYPE:
			imagPart = complexVal.ImagPart
		}

		// Create new complex number with inexact parts
		newComplex := num.Complex{
			RealPart: realPart,
			ImagPart: imagPart,
		}
		return &obj.NumberObject{Value: newComplex}
	}

	return &Error{Message: "exact->inexact: unsupported number type"}
}

// InexactToExact converts an inexact number to an exact number
func InexactToExact(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "inexact->exact requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "inexact->exact argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		// Already exact, return as is
		return &obj.NumberObject{Value: numObj}

	case num.RATIONAL_TYPE:
		// Already exact, return as is
		return &obj.NumberObject{Value: numObj}

	case num.REAL_TYPE:
		// Convert real to rational
		realVal := numObj.(num.Real).Value
		rat := floatToRational(realVal)
		return &obj.NumberObject{Value: rat}

	case num.COMPLEX_TYPE:
		// Convert complex with inexact parts to exact parts
		complexVal := numObj.(num.Complex)

		// Convert real part
		var realPart num.RNumber
		switch complexVal.RealPart.Type() {
		case num.INTEGER_TYPE:
			realPart = complexVal.RealPart
		case num.RATIONAL_TYPE:
			realPart = complexVal.RealPart
		case num.REAL_TYPE:
			realVal := complexVal.RealPart.(num.Real).Value
			rat := floatToRational(realVal)
			realPart = rat
		}

		// Convert imaginary part
		var imagPart num.RNumber
		switch complexVal.ImagPart.Type() {
		case num.INTEGER_TYPE:
			imagPart = complexVal.ImagPart
		case num.RATIONAL_TYPE:
			imagPart = complexVal.ImagPart
		case num.REAL_TYPE:
			realVal := complexVal.ImagPart.(num.Real).Value
			rat := floatToRational(realVal)
			imagPart = rat
		}

		// Create new complex number with exact parts
		newComplex := num.Complex{
			RealPart: realPart,
			ImagPart: imagPart,
		}
		return &obj.NumberObject{Value: newComplex}
	}

	return &Error{Message: "inexact->exact: unsupported number type"}
}

// Exp computes the exponential of a number (e^z)
func Exp(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "exp requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "exp argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		intVal := numObj.(num.Integer).Value
		result := math.Exp(float64(intVal))
		return &obj.NumberObject{Value: num.Real{Value: result}}

	case num.RATIONAL_TYPE:
		rat := numObj.(num.Rational)
		floatVal := float64(rat.Numerator) / float64(rat.Denominator)
		result := math.Exp(floatVal)
		return &obj.NumberObject{Value: num.Real{Value: result}}

	case num.REAL_TYPE:
		realVal := numObj.(num.Real).Value
		result := math.Exp(realVal)
		return &obj.NumberObject{Value: num.Real{Value: result}}

	case num.COMPLEX_TYPE:
		// For complex numbers z = a + bi, e^z = e^a * (cos(b) + i*sin(b))
		complexVal := numObj.(num.Complex)

		// Convert real and imaginary parts to float64
		var realPart, imagPart float64

		switch complexVal.RealPart.Type() {
		case num.INTEGER_TYPE:
			realPart = float64(complexVal.RealPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.RealPart.(num.Rational)
			realPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			realPart = complexVal.RealPart.(num.Real).Value
		}

		switch complexVal.ImagPart.Type() {
		case num.INTEGER_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.ImagPart.(num.Rational)
			imagPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			imagPart = complexVal.ImagPart.(num.Real).Value
		}

		// Calculate e^a
		expReal := math.Exp(realPart)

		// Calculate cos(b) and sin(b)
		cosB := math.Cos(imagPart)
		sinB := math.Sin(imagPart)

		// Result is e^a * (cos(b) + i*sin(b))
		realResult := expReal * cosB
		imagResult := expReal * sinB

		return &obj.NumberObject{Value: num.Complex{
			RealPart: num.Real{Value: realResult},
			ImagPart: num.Real{Value: imagResult},
		}}
	}

	return &Error{Message: "exp: unsupported number type"}
}

// Log computes the natural logarithm of a number (ln z)
func Log(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "log requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "log argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		intVal := numObj.(num.Integer).Value
		if intVal <= 0 {
			// For non-positive integers, result is complex
			floatVal := float64(-intVal)
			logVal := math.Log(floatVal)
			// For negative numbers, ln(z) = ln|z| + πi
			if intVal < 0 {
				return &obj.NumberObject{Value: num.Complex{
					RealPart: num.Real{Value: logVal},
					ImagPart: num.Real{Value: math.Pi},
				}}
			}
			// For zero, return -∞
			return &obj.NumberObject{Value: num.Real{Value: math.Inf(-1)}}
		}
		result := math.Log(float64(intVal))
		return &obj.NumberObject{Value: num.Real{Value: result}}

	case num.RATIONAL_TYPE:
		rat := numObj.(num.Rational)
		floatVal := float64(rat.Numerator) / float64(rat.Denominator)
		if floatVal <= 0 {
			// For non-positive rationals, result is complex
			floatVal = -floatVal
			logVal := math.Log(floatVal)
			// For negative numbers, ln(z) = ln|z| + πi
			if rat.Numerator < 0 {
				return &obj.NumberObject{Value: num.Complex{
					RealPart: num.Real{Value: logVal},
					ImagPart: num.Real{Value: math.Pi},
				}}
			}
			// For zero, return -∞
			return &obj.NumberObject{Value: num.Real{Value: math.Inf(-1)}}
		}
		result := math.Log(floatVal)
		return &obj.NumberObject{Value: num.Real{Value: result}}

	case num.REAL_TYPE:
		realVal := numObj.(num.Real).Value
		if realVal < 0 {
			// For negative reals, result is complex
			logVal := math.Log(-realVal)
			// ln(z) = ln|z| + πi for negative z
			return &obj.NumberObject{Value: num.Complex{
				RealPart: num.Real{Value: logVal},
				ImagPart: num.Real{Value: math.Pi},
			}}
		} else if realVal == 0 {
			// For zero, return -∞
			return &obj.NumberObject{Value: num.Real{Value: math.Inf(-1)}}
		}
		result := math.Log(realVal)
		return &obj.NumberObject{Value: num.Real{Value: result}}

	case num.COMPLEX_TYPE:
		// For complex numbers z = a + bi, ln(z) = ln|z| + i*arg(z)
		// where |z| = sqrt(a² + b²) and arg(z) = atan2(b, a)
		complexVal := numObj.(num.Complex)

		// Convert real and imaginary parts to float64
		var realPart, imagPart float64

		switch complexVal.RealPart.Type() {
		case num.INTEGER_TYPE:
			realPart = float64(complexVal.RealPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.RealPart.(num.Rational)
			realPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			realPart = complexVal.RealPart.(num.Real).Value
		}

		switch complexVal.ImagPart.Type() {
		case num.INTEGER_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.ImagPart.(num.Rational)
			imagPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			imagPart = complexVal.ImagPart.(num.Real).Value
		}

		// Calculate |z| = sqrt(a² + b²)
		modulus := math.Sqrt(realPart*realPart + imagPart*imagPart)

		// Calculate arg(z) = atan2(b, a)
		argument := math.Atan2(imagPart, realPart)

		// Result is ln|z| + i*arg(z)
		realResult := math.Log(modulus)
		imagResult := argument

		// If imaginary part is zero, return real number
		if imagResult == 0 {
			return &obj.NumberObject{Value: num.Real{Value: realResult}}
		}

		return &obj.NumberObject{Value: num.Complex{
			RealPart: num.Real{Value: realResult},
			ImagPart: num.Real{Value: imagResult},
		}}
	}

	return &Error{Message: "log: unsupported number type"}
}

// Sin returns the sine of a number
func Sin(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "sin requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "sin argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		intVal := numObj.(num.Integer).Value
		result := math.Sin(float64(intVal))
		return &obj.NumberObject{Value: num.Real{Value: result}}

	case num.RATIONAL_TYPE:
		ratVal := numObj.(num.Rational)
		// Convert to float for calculation
		floatVal := float64(ratVal.Numerator) / float64(ratVal.Denominator)
		result := math.Sin(floatVal)
		return &obj.NumberObject{Value: num.Real{Value: result}}

	case num.REAL_TYPE:
		realVal := numObj.(num.Real).Value
		result := math.Sin(realVal)
		return &obj.NumberObject{Value: num.Real{Value: result}}

	case num.COMPLEX_TYPE:
		// For complex numbers z = a + bi, sin(z) = sin(a)cosh(b) + i*cos(a)sinh(b)
		complexVal := numObj.(num.Complex)

		// Convert real and imaginary parts to float64
		var realPart, imagPart float64

		switch complexVal.RealPart.Type() {
		case num.INTEGER_TYPE:
			realPart = float64(complexVal.RealPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.RealPart.(num.Rational)
			realPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			realPart = complexVal.RealPart.(num.Real).Value
		}

		switch complexVal.ImagPart.Type() {
		case num.INTEGER_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.ImagPart.(num.Rational)
			imagPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			imagPart = complexVal.ImagPart.(num.Real).Value
		}

		// Calculate sin(a)cosh(b) + i*cos(a)sinh(b)
		realResult := math.Sin(realPart) * math.Cosh(imagPart)
		imagResult := math.Cos(realPart) * math.Sinh(imagPart)

		// If imaginary part is zero, return real number
		if imagResult == 0 {
			return &obj.NumberObject{Value: num.Real{Value: realResult}}
		}

		return &obj.NumberObject{Value: num.Complex{
			RealPart: num.Real{Value: realResult},
			ImagPart: num.Real{Value: imagResult},
		}}
	}

	return &Error{Message: "sin: unsupported number type"}
}

// Cos returns the cosine of a number
func Cos(args []obj.Object, env *obj.Environment) obj.Object {
	if len(args) != 1 {
		return &Error{Message: "cos requires exactly 1 argument"}
	}

	if args[0].Type() != obj.NUMBER_OBJ {
		return &Error{Message: "cos argument must be a number"}
	}

	numObj := args[0].(*obj.NumberObject).Value

	switch numObj.Type() {
	case num.INTEGER_TYPE:
		intVal := numObj.(num.Integer).Value
		result := math.Cos(float64(intVal))
		return &obj.NumberObject{Value: num.Real{Value: result}}

	case num.RATIONAL_TYPE:
		ratVal := numObj.(num.Rational)
		// Convert to float for calculation
		floatVal := float64(ratVal.Numerator) / float64(ratVal.Denominator)
		result := math.Cos(floatVal)
		return &obj.NumberObject{Value: num.Real{Value: result}}

	case num.REAL_TYPE:
		realVal := numObj.(num.Real).Value
		result := math.Cos(realVal)
		return &obj.NumberObject{Value: num.Real{Value: result}}

	case num.COMPLEX_TYPE:
		// For complex numbers z = a + bi, cos(z) = cos(a)cosh(b) - i*sin(a)sinh(b)
		complexVal := numObj.(num.Complex)

		// Convert real and imaginary parts to float64
		var realPart, imagPart float64

		switch complexVal.RealPart.Type() {
		case num.INTEGER_TYPE:
			realPart = float64(complexVal.RealPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.RealPart.(num.Rational)
			realPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			realPart = complexVal.RealPart.(num.Real).Value
		}

		switch complexVal.ImagPart.Type() {
		case num.INTEGER_TYPE:
			imagPart = float64(complexVal.ImagPart.(num.Integer).Value)
		case num.RATIONAL_TYPE:
			rat := complexVal.ImagPart.(num.Rational)
			imagPart = float64(rat.Numerator) / float64(rat.Denominator)
		case num.REAL_TYPE:
			imagPart = complexVal.ImagPart.(num.Real).Value
		}

		// Calculate cos(a)cosh(b) - i*sin(a)sinh(b)
		realResult := math.Cos(realPart) * math.Cosh(imagPart)
		imagResult := -math.Sin(realPart) * math.Sinh(imagPart)

		// If imaginary part is zero, return real number
		if imagResult == 0 {
			return &obj.NumberObject{Value: num.Real{Value: realResult}}
		}

		return &obj.NumberObject{Value: num.Complex{
			RealPart: num.Real{Value: realResult},
			ImagPart: num.Real{Value: imagResult},
		}}
	}

	return &Error{Message: "cos: unsupported number type"}
}
